Playwrightのパラメータ付きFixtureで効率的なテストパターン管理を実現する

Playwrightのパラメータ付きFixtureで効率的なテストパターン管理を実現する

Playwrightのパラメータ付きFixtureについて、キーワードによる検索実行を例に実装方法を解説します。Fixtureにパラメータを渡せるようにすることで、類似したテストケースの再利用性を高め、テストコードの保守性を向上できます。
Clock Icon2024.12.14

情報システム室の進地@日比谷です。

先日書いた次の記事で、PlaywrightにてFixtureを使うことで画面状態を共有、再利用することができることをご案内しました。

Playwright fixture 活用術:テスト間での画面状態の再利用パターン

しかし、再利用の観点からすると、Fixtureに対してパラメータを渡したいということはままある要望かと思います。例えば、検索ワードが違うだけの検索結果画面をそれぞれチェックする場合など、検索ワードをパラメータとしてFixtureに渡せると嬉しいです。

そこで、Fixtureにパラメータを渡す方法として、パラメータ付きFixtureを実装する方法をご紹介します。

パラメータを受ける関数を返すFixtureを定義する

Fixtureでは、パラメータを受け取って処理したPageオブジェクトを返す非同期関数を返すように定義します。
次のコード例では検索キーワードをパラメータで受け取り、検索結果のPageオブジェクトを返す非同期関数を記述し、それをawait use()に与えることでパラメータ付きFixtureを定義しています。

// tests/fixtures/search.ts
import { test as base } from '@playwright/test';
import { Page } from '@playwright/test';

// カスタムフィクスチャの型定義
type CustomFixture = {
  searchedPage: (keyword: string) => Promise<Page>;
};

// フィクスチャの拡張
export const test = base.extend<CustomFixture>({
  // 検索結果の状態を得る
  searchedPage: await async ({ page }, use) => {
    await use(async (keyword: string) => {
      // テスト対象のページへアクセスする
      await page.goto('https://example.com/example.html');

      // 例)パラメータで受け取ったkeywordで検索実行する
      await page.fill('#keyword', keyword);
      await page.getByRole('button', { name: '検索する' }).click();

      return page;
    });
  }
});

export { expect } from '@playwright/test';

このFixtureの使い方は次の通りです。

import { test, expect } from './fixtures/search';

// testを並列実行する
test.describe.configure({ mode: 'parallel' });

test.describe('検索結果に対するテスト群', () => {
  test('タイトルに「クラスメソッド」が含まれている', async ({ searchedPage }) => {
    const classmethodSearchedPage = await searchedPage('クラスメソッド');
    expect.soft(await classmethodSearchedPage.locator('.title'), `「クラスメソッド」が見つかりません`).toHaveText(/クラスメソッド/);
  });
  test('タイトルに「アノテーション」が含まれている', async ({ searchedPage }) => {
    const annotationSearchedPage = await searchedPage('アノテーション');
    expect.soft(await annotationSearchedPage.locator('.title'), `「アノテーション」が見つかりません`).toHaveText(/アノテーション/);
  });
});

Pageオブジェクト以外の複雑なオブジェクトも返せる

パラメータ付きFixtureには、通常のFixtureと同様に様々な複雑な構造のオブジェクトも返すことができます。
例えば、検索結果を表す値オブジェクトをSearchResultとして定義して、それを返すこともできます。

// tests/models/SearchResult.ts
export class SearchResult {
  private constructor(
    private readonly titles: readonly string[]
  ) {
    this.titles = titles;
  }

  // ファクトリーメソッド
  static create(titles: string[]): SearchResult {
    return new SearchResult(titles);
  }

  // ゲッター
  getTitles(): readonly string[] {
    return this.titles;
  }
}

このオブジェクトを返すパラメータ付きFixtureは例えば次のように定義します。

// tests/fixtures/search.ts
import { test as base } from '@playwright/test';
import { Page } from '@playwright/test';

// カスタムフィクスチャの型定義
type CustomFixture = {
  searchedPage: (keyword: string) => Promise<SearchResult>;
};

// フィクスチャの拡張
export const test = base.extend<CustomFixture>({
  // 検索結果の状態を得る
  searchedPage: await async ({ page }, use) => {
    await use(async (keyword: string) => {
      // テスト対象のページへアクセスする
      await page.goto('https://example.com/example.html');

      // 例)パラメータで受け取ったkeywordで検索実行する
      await page.fill('#keyword', keyword);
      await page.getByRole('button', { name: '検索する' }).click();

      // 例)検索結果からリンクテキストを取得(検索結果がテーブル組で表示されていると仮定)
      const anchors = await page.locator('tbody tr td a').all();
      const titles: string[] = [];
      for await (const anchor of anchors) {
        titles.push(await anchor.textContent());
      }

      // SearchResultオブジェクトを返す
      return SearchResult.create(titles);
    });
  }
});

export { expect } from '@playwright/test';

使い方は次の通りです。

import { test, expect } from './fixtures/search';

// testを並列実行する
test.describe.configure({ mode: 'parallel' });

test.describe('検索結果に対するテスト群', () => {
  test('「クラスメソッド」の検索結果が10件ある', async ({ searchedPage }) => {
    const classmethodSearchResult: SearchResult = await searchedPage('クラスメソッド');
    expect.soft(classmethodSearchResult.getTitles().length, `「クラスメソッド」の検索結果が10件ではありません`).toBe(10);
  });
  test('「アノテーション」の検索結果が5件ある', async ({ searchedPage }) => {
    const annotationSearchResult: SearchResult = await searchedPage('アノテーション');
    expect.soft(annotationSearchResult.getTitles().length, `「アノテーション」の検索結果が5件ではありません`).toBe(5);
  });
});

まとめ

パラメータ付きFixtureを使うことでE2Eテストにおける同一、類似操作の集約や再利用などをさらに進めることができました。テストコードの保守性の向上に繋がりますのでぜひ活用してみてください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.